﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syngine.Components.Animations;
using Syngine.Content;

namespace Syngine.Components
{
	/// <summary>
	/// 
	/// </summary>
	public abstract class Drawable : Loadable, IDraw
	{
		#region Fields

		protected Vector2 InternalPosition;
		private bool _visible;
		private int _drawOrder;

		#endregion

		#region Constructors

		protected Drawable()
		{
			Scale = 1.0f;
			Visible = true;
			IsPausable = true;
			Color = Color.White;
			Effects = SpriteEffects.None;
			AnimationPlayer = new AnimationPlayer();
			Drawables = new DrawableList();
		}

		#endregion

		#region Properties

		/// <summary>
		/// 
		/// </summary>
		public event EventHandler<EventArgs> DrawOrderChanged;

		/// <summary>
		/// 
		/// </summary>
		public event EventHandler<EventArgs> VisibleChanged;

		/// <summary>
		/// 
		/// </summary>
		public int DrawOrder
		{
			get
			{
				return _drawOrder;
			}
			set
			{
				if (_drawOrder != value)
				{
					_drawOrder = value;
					OnDrawOrderChanged();
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		public bool Visible
		{
			get
			{
				return _visible;
			}
			set
			{
				if (_visible != value)
				{
					_visible = value;
					OnVisibleChanged();
				}
				_visible = value;
			}
		}

		/// <summary>
		/// Gets or sets the position.
		/// </summary>
		/// <value>The position.</value>
		public virtual Vector2 Position
		{
			get
			{
				return InternalPosition;
			}
			set
			{
				InternalPosition = value;
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether show the bounds.
		/// </summary>
		/// <value><c>true</c> if [show bounds]; otherwise, <c>false</c>.</value>
		public bool ShowBounds { get; set; }

		/// <summary>
		/// Gets or sets the depth.
		/// </summary>
		/// <value>The depth.</value>
		public float Depth { get; set; }

		/// <summary>
		/// Gets or sets the rotation.
		/// </summary>
		/// <value>The rotation.</value>
		public float Rotation { get; set; }

		/// <summary>
		/// Gets or sets the scale.
		/// </summary>
		/// <value>The scale.</value>
		public float Scale { get; set; }

		/// <summary>
		/// Gets or sets the origin.
		/// </summary>
		/// <value>The origin.</value>
		public Vector2 Origin { get; set; }

		/// <summary>
		/// Gets or sets the color.
		/// </summary>
		/// <value>The color.</value>
		public Color Color { get; set; }

		/// <summary>
		/// Gets or sets the bounds.
		/// </summary>
		/// <value>The bounds.</value>
		public Rectangle Bounds { get; set; }

		/// <summary>
		/// Gets or sets the source rectangle.
		/// </summary>
		/// <value>The source rectangle.</value>
		public Rectangle? SourceRectangle { get; set; }

		/// <summary>
		/// Gets or sets the texture.
		/// </summary>
		/// <value>The texture.</value>
		public Asset<Texture2D> Texture { get; set; }

		/// <summary>
		/// Gets or sets the effects.
		/// </summary>
		/// <value>The effects.</value>
		public SpriteEffects Effects { get; set; }

		/// <summary>
		/// Gets the animation player.
		/// </summary>
		/// <value>The animation player.</value>
		protected AnimationPlayer AnimationPlayer { get; private set; }

		/// <summary>
		/// Gets the list of child drawable items.
		/// </summary>
		/// <value>The drawables.</value>
		protected DrawableList Drawables { get; private set; }

		#endregion

		#region Methods

		public virtual void Draw(DrawContext context)
		{
			if (Visible)
			{
				DrawCore(context);
				Drawables.Draw(context);
			}
		}

		public override void LoadContent(LoadContext context)
		{
			Texture = GetAsset(context);

			if (Texture != null)
			{
				var bounds = Texture.Bounds();
				Bounds = new Rectangle((int)Position.X, (int)Position.Y, bounds.Width, bounds.Height);
				Origin = new Vector2(bounds.Width / 2.0f, bounds.Height / 2.0f);
			}

			Drawables.LoadContent(context);
		}

		public override void UnloadContent()
		{
			if (Texture != null)
			{
				Texture.Dispose();
			}

			Drawables.UnloadContent();
		}

		protected virtual Texture2D GetAsset(LoadContext context)
		{
			return GetAsset<Texture2D>(context);
		}

		protected virtual T GetAsset<T>(LoadContext context)
		{
			return default(T);
		}

		protected virtual T GetAsset<T>(LoadContext context, string assetName)
		{
			return default(T);
		}

		protected virtual void DrawCore(DrawContext context)
		{
			if (Texture != null)
			{
				context.Draw(Texture, Position, SourceRectangle, Color, Rotation, Origin, Scale, Effects, Depth);
			}

			if (ShowBounds)
			{
				Bounds.ToDrawable(Color).Draw(context);

				if (SourceRectangle != null)
				{
					SourceRectangle.Value.ToDrawable(Color).Draw(context);
				}
			}
		}

		protected override void UpdateCore(UpdateContext context)
		{
			var x = (int)(Position.X - Origin.X);
			var y = (int)(Position.Y - Origin.Y);
			Bounds = new Rectangle(x, y, Bounds.Width, Bounds.Height);
		}

		protected void Add(IDraw drawable)
		{
			Drawables.Add(drawable);
		}

		protected void Add<TItem>(TItem item) where TItem : IDraw, IUpdate
		{
			Drawables.Add(item);
			Updatables.Add(item);
		}

		private void OnVisibleChanged()
		{
			var handler = VisibleChanged;

			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}

		private void OnDrawOrderChanged()
		{
			var handler = DrawOrderChanged;

			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}

		#endregion
	}
}